package data_structs;

import errors.IncorrectNucleotide;

public class SequenceWrapper implements Comparable<SequenceWrapper>{

	private String sequence;
	private int intSequence;
	private int id;
	private int position;
	private int treefixer;
	private boolean complement;
	
	public SequenceWrapper(String sequence, int position, boolean complement) throws IncorrectNucleotide {
		this("0",sequence, position, complement);
	}
	
	public SequenceWrapper(String id, String sequence, int position, boolean complement) throws IncorrectNucleotide {
		this.sequence = sequence.toUpperCase();
		this.id = Integer.parseInt(id);
		this.position = position;
		this.complement = complement;
		//Convert sequence into an int
		this.intSequence = parseToInt(convertToNumbers(this.sequence));
	}
	
	private String convertToNumbers(String s) throws IncorrectNucleotide {
		String tmp = "";
		//Iterate through sequence and convert into an integer string
		int i;
		for(i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			switch(c) {
				case 'A': tmp += "1"; break;
				case 'T': tmp += "2"; break;
				case 'C': tmp += "3"; break;
				case 'G': tmp += "4"; break;
				case 'N': tmp += "5"; break;
				default: throw new IncorrectNucleotide("Nucleotide '" + c + "' is not valid.");
			}
		}
		return tmp;
	}
	
	private int parseToInt(String number) {
		int val = 0;
		int len = number.length();
		//Length of tmp is small enough to fit in int
		if( len < 8 )
			return Integer.parseInt(number);
		
		//Length of tmp is too big to fit in int
		int end = len;
		int begin = 0;
		do {
			val += Integer.parseInt(number.substring(begin, begin+8));
			end -= 8;
			begin += 8;
		} while( end > 8 );
		
		//If any characters left over, convert the remaining
		if( len > begin )
			val += Integer.parseInt(number.substring(begin, len));
		return val;
	}
	
	//Accessor methods
	public String getSequence() {
		return sequence;
	}
	
	public int getIntSequence() {
		return intSequence;
	}
	
	public int getID(){
		return id;
	}
	
	public int getPosition() {
		return position;
	}
	
	public boolean isComplement() {
		return complement;
	}
	
	public void increaseTreeFixer() {
		treefixer++;
	}
	
	public int getTreeFixer() {
		return treefixer;
	}
	
	public int hashCode() {
//		int a, b, c;
//		a = b = c = 0;
//		String seq = sequence;
//		int len = seq.length();
//		int idx = 0;
//		int[] tmp = {a,b,c};
//		while( len > 12 ) {
//			tmp[0] += seq.charAt(idx++);
//			tmp[1] += seq.charAt(idx++);
//			tmp[2] += seq.charAt(idx++);
//			tmp = mix(tmp[0],tmp[1],tmp[2]);
//			len -= 12;
//		}
//		
//		a=tmp[0]; b=tmp[1]; c=tmp[2];
//		
//		switch(len){
//			case 12: a+=seq.charAt(idx++); b+=seq.charAt(idx++); c+=seq.charAt(idx++); break;
//		    case 11: a+=seq.charAt(idx++); b+=seq.charAt(idx++); c+=seq.charAt(idx++)&0xffffff; break;
//		    case 10: a+=seq.charAt(idx++); b+=seq.charAt(idx++); c+=seq.charAt(idx++)&0xffff; break;
//		    case 9 : a+=seq.charAt(idx++); b+=seq.charAt(idx++); c+=seq.charAt(idx++)&0xff; break;
//		    case 8 : a+=seq.charAt(idx++); b+=seq.charAt(idx++); break;
//		    case 7 : a+=seq.charAt(idx++); b+=seq.charAt(idx++)&0xffffff; break;
//		    case 6 : a+=seq.charAt(idx++); b+=seq.charAt(idx++)&0xffff; break;
//		    case 5 : a+=seq.charAt(idx++); b+=seq.charAt(idx++)&0xff; break;
//		    case 4 : a+=seq.charAt(idx++); break;
//		    case 3 : a+=seq.charAt(idx++)&0xffffff; break;
//		    case 2 : a+=seq.charAt(idx++)&0xffff; break;
//		    case 1 : a+=seq.charAt(idx++)&0xff; break;
//		    case 0 : return (int) c;
//		}
//		
//		c = finalMix(a,b,c);
//		return c;
		
		
		return intSequence;
//		return sequence.hashCode();
	}
	
//	private int[] mix(int a, int b, int c) {
//		int[] res = new int[3];
//		a -= c;  a ^= rot(c, 4);  c += b;
//		b -= a;  b ^= rot(a, 6);  a += c;
//		c -= b;  c ^= rot(b, 8);  b += a;
//		a -= c;  a ^= rot(c,16);  c += b;
//		b -= a;  b ^= rot(a,19);  a += c;
//		c -= b;  c ^= rot(b, 4);  b += a;
//		res[0] = a; res[1] = b; res[2] = c;
//		return res;
//	}
//	
//	private int finalMix(int a, int b, int c) {
//		c ^= b; c -= rot(b,14);
//		a ^= c; a -= rot(c,11);
//		b ^= a; b -= rot(a,25);
//		c ^= b; c -= rot(b,16);
//		a ^= c; a -= rot(c,4); 
//		b ^= a; b -= rot(a,14);
//		c ^= b; c -= rot(b,24);
//		return (int) c & 0x7fffffff;
//	}
//	
//	private int rot(int x, int k) {	
//		return (((x)<<(k)) | ((x)>>(32-(k))));
//	}
	
	public String toString() {
		return this.sequence;
	}
	
	//Use String's compareTo method if int sequences are same
	@Override
	public int compareTo(SequenceWrapper obj) {
		if( (this.getIntSequence()-obj.getIntSequence()) == 0 )
			return this.getSequence().compareTo(obj.getSequence());
		
		return -1;
	}
}
